/*
 * anima/pdb.hh - Physical/real pages database interface.
 * Copyright (C) 2007  Mikhail Vorozhtsov
 * See the LICENSE section of the README file for details.
 */

/* $Id$ */

#ifndef HEADER_ANIMA_PDB_HH
#define HEADER_ANIMA_PDB_HH

#include ANIMA_ARCH_HEADER (pdb.hh)
#include HEADER (anima,mm.hh)
#include HEADER (anima,firmware.hh)
#include HEADER (anima,object.hh)
#include HEADER (elbp,elbp.hh)
#include HEADER (LIBBST_PREFIX,parent_node.hh)
#include HEADER (LIBBST_PREFIX,avl_tree.hh)
#include HEADER (utils,setters.hh)

START_NAMESPACE (anima)

class pdb : public ANIMA_ARCH::pdb
{
  friend class mm;

public:
  struct region_t : public firmware::region_t
  {
    friend class pdb;

  protected:
    LIBBST_NS::basic_avl_node_t<LIBBST_NS::basic_parent_next_node_t> node;

  public:
    INLINE
    region_t (u64_t addr, u64_t size, type_t type)
    {
      this->addr = addr;
      this->size = size;
      this->type = type;
    }

  public:
    const region_t *next () const;

    INLINE
    bool
    operator== (const region_t &region) const
    {
      return addr <= region.addr && region.addr - addr < size
             || region.addr < addr && addr - region.addr < region.size;
    }

    INLINE
    bool
    operator< (const region_t &region) const
    {
      return addr <= region.addr && region.addr - addr >= size;
    }

    INLINE
    bool
    operator== (u64_t addr) const
    {
      return this->addr <= addr && addr - this->addr < size;
    }

    INLINE
    bool
    operator< (u64_t addr) const
    {
      return this->addr < addr && addr - this->addr >= size;
    }
  };

public:
  union entry_t
  {
    enum usage_t
    {
      USAGE_NONE = 0,
      USAGE_KERNEL = 1,
      USAGE_CPU = 2,
      USAGE_SPACE = 3,
      USAGE_THREAD = 4,
      USAGE_USER = 5
    };

    enum set_t
    {
      SET_SPACE = 0,
      SET_RESERVED = 1,
      SET_THREAD = 2,
      SET_SPECIAL = 3
    };

    struct
      {
        u64_t P : 1;
        u64_t usage : 3;
        u64_t set : 2;
        u64_t : mm::PAGE_BITS - 6;
        u64_t base : 64 - mm::PAGE_BITS;
      };
    u64_t raw;

    UTILS_RAW_ZERO (entry_t, u64_t)

    UTILS_BOOL_SETTER (entry_t, P)
    UTILS_SETTER (entry_t, usage_t, usage)
    UTILS_SETTER (entry_t, set_t, set)
    UTILS_SETTER (entry_t, u64_t, base)

    INLINE
    u64_t
    get_ptr ()
    {
      return (u64_t) base << mm::PAGE_BITS;
    }

    INLINE
    entry_t &
    set_ptr (u64_t value)
    {
      base = value >> mm::PAGE_BITS;
      return *this;
    }
  };

  struct lvl1e_t
  {
    entry_t entry;
    space_ref_t owner;
    u64_t shared_with : object_t::BITS - mm::PAGE_BITS;
    u64_t maps_count : 64 - object_t::BITS + mm::PAGE_BITS;
    space_ref_t first_share_space;
    u64_t : 64;
    u64_t : 64;
    lvl1e_t *prev;
    lvl1e_t *next;
  };

  union paddr_t
  {
    struct
      {
        u64_t offset : mm::PAGE_BITS;
        u64_t LVL1 : mm::PAGE_BITS - 6;
        u64_t LVL2 : mm::PAGE_BITS - 3;
        u64_t LVL3 : mm::PAGE_BITS - 3;
        u64_t LVL4 : mm::PAGE_BITS - 3;
        u64_t LVL5 : pdb::ADDR_BITS + 15 - 5 * mm::PAGE_BITS;
        u64_t : 64 - pdb::ADDR_BITS;
      };
    u64_t raw;

    UTILS_RAW_ZERO (paddr_t, u64_t)
  };

private:
  static LIBBST_NS::avl_tree_t<
           LIBBST_NS::parent_next_node_t<
             region_t, LIBBST_NS::basic_avl_node_t<
                         LIBBST_NS::basic_parent_next_node_t>,
             offsetof (region_t, node)>,
           LIBBST_NS::basic_min_tree_t, LIBBST_NS::min_glue_t> regions;

public:
  static bool init (const elbp::info_t *elbp_info);
  static void grant ();
  static bool reap ();

public:
  static bool add_region (u64_t addr, u64_t size, region_t::type_t type);
  static const region_t *first_region ();
  static const region_t *find_region (u64_t addr);

public:
  static lvl1e_t *get (u64_t addr);
  static lvl1e_t *add (u64_t addr, bool used_p = false);

  INLINE
  static bool
  contains_p (u64_t addr)
  {
    return get (addr) != NULL;
  }

private:
  static bool page_used_p (u64_t addr);
  static bool fill ();

public:
  static lvl1e_t *allocate ();
  static lvl1e_t *allocate (u64_t addr);
  static void deallocate (lvl1e_t *lvl1e);
};

END_NAMESPACE

#endif /* HEADER_ANIMA_PDB_HH */

